'use strict'

const http=require('http');
const fs=require('fs');
const fsp=fs.promises;

function parseBodyHeaders(data){
    let headers={};
    let dsplit=data.split('\r\n').filter(p=>p.length>0);
    let ind;
    let k;
    for(let d of dsplit){
        ind=d.indexOf(':')
        if(ind<=0) continue;
        k=d.substring(0,ind).toLowerCase();
        headers[k]=d.substring(ind+1).trim();  //trim():取去除空格
    }
    let cpd=headers['content-disposition'];
    //按照;分隔成数组，第一个元素form-data去掉
    let clist=cpd.split(';').slice(1);
    let name,filename;
    for(let a of clist){
        ind=a.indexOf('filename="');
        if(ind>=0){
            filename=a.substring(ind+10,a.length-1)
        }else{
            name=a.substring(6,a.length-1);
        }
    }
    return {
        headers,
        name,
        filename
    }

}

/**
 * 
 * @param {Buffer} bodyData 原始的body数据
 * @param {Object} reqHeaders request.headers
 */

function parseBody(bodyData,reqHeaders){
    // content-type:multipart/form-DataTransfer;boundary=-------34294329
    let ctype=reqHeaders['content-type'];

    //拿到分界线boundary的数据
    let boundary=ctype.substring(ctype.indexOf('=')+1);

    //数据中用于分割文件的分界线(又在前面多加了两个-)
    // boundary=`--${boundary}\r\n`;
    let crlf_bdy=`\r\n--${boundary}`;
    //从偏移boundary的长度开始查找下一个分界线的位置
    let data_end_index=bodyData.indexOf(crlf_bdy,crlf_bdy.length);
    //body消息数据中header部分的最后索引
    let header_end_index=bodyData.indexOf('\r\n\r\n',crlf_bdy.length);
    //(编码格式，开始的索引值，结束的位置)
    let header_data=bodyData.toString('utf-8',crlf_bdy.length,header_end_index);

    //解析body数据中的消息头
    let hd=parseBodyHeaders(header_data);

    let fileinfo={
        start:header_end_index+4,
        end:data_end_index
    }
    fileinfo.length=fileinfo.end-fileinfo.start

    return {
        name:hd.name,
        filename:hd.filename,
        headers:hd.headers,
        start:fileinfo.start,
        end:fileinfo.end,
        length:fileinfo.length

    }
}

//返回文件后缀名
function parseExtName(filename){
    if(filename.length<2) return ''; //说明文件名就只有一个'.'
    let namesplit=filename.split('.').filter(p=>p.length>0)  //filter--创建一个新数组, 由通过函数测试的所有元素组成
    if(namesplit.length<2) return '';  //此时认为是只有后缀名的情况
    return `.${namesplit[namesplit.length-1]}`
}


let routerTable={
    GET:{   // GET /upload HTTP/1.1
        '/upload':async(ctx)=>{
            let stm=fs.createReadStream('./upload.html');
            stm.pipe(ctx.res);
            stm.on('end',()=>{
                ctx.end();  //为什么是ctx.end()
            })
        }
    },
    POST:{   // POST /upload HTTP/1.1
        '/upload':async(ctx)=>{
            //下面直到fh.close()这段代码为创建一个文件，写入ctx.rawBody中的值
            console.log(ctx.files);
            let finfo=ctx.files;
            let filename=`${Date.now()}-${parseInt(Math.random()*100000)+100000}`
                        +parseExtName(finfo.filename);
            let fh=await fsp.open(filename,'w+'); //打开一个文件,'w+':打开一个可读可写，如果没有就创建，如果里面有内容就清空重写
            let fret=await fh.write(ctx.rawBody,finfo.start,finfo.length,0);
            //关闭打开的文件
            fh.close()

            //返回写入的字节数，string或buffer类型，不能是其他类型
            ctx.end(`${fret.bytesWritten}`);  //调用ctx对象中的end方法
        }
    }
}

/**------------------------  START SERVER   -------------------------- */

let serv=http.createServer();
serv.on('request',(request,response)=>{
    let rm=routerTable[request.method];

    let usplit=request.url.split('?');  //完整的路径可能会含有参数，例如：http://a.com:1234/xyz?a=123 ,request.url='/xyz?a=123'
    let pathname=usplit[0];
    let querystring=usplit[1] || '';  //左侧为true就返回左

    if(!rm || !rm[pathname]){  //既不是GET也不是POST,或者不满足request.url为'/upload'
        response.statusCode=404;
        response.end('page not found');
        return
    }

    //ctx is context,创建请求上下文对象
    let ctx={
        req:request,
        res:response,
        method:request.method,
        headers:request.headers,
        end:(data,encoding='utf-8')=>{  
            response.end(data,encoding)  //encoding--对应data的字符编码
        },
        write:(data,encoding='utf-8')=>{
            response.write(data,encoding)
        },
        path:pathname,
        querystring:querystring,
        rawBody:null,  //rawBody:原始的body数据  (对象的初始化为null)
    }

    let bufferList=[];
    let totalLength=0;
    //请求类型：POST PUT PATCH DELETE   --前三个必须提交body数据，DELETE可选
    //         GET HEAD TRACE OPTIONS  --不能提交body数据
    if('PD'.indexOf(request.method[0])>=0){  //request.method[0]在字符串'PD'中的index值
        //必须提交body数据的四种请求类型为P或D开头的，它们在'PD'字符串中的index值不是0就是1，如果不是以P或D开头的请求index值为-1
        request.on('data',chunk=>{
            totalLength+=chunk.length;
            bufferList.push(chunk);
        })
    }else{  //其他不能提交数据的请求类型如果也提交了body数据
        request.on('data',chunk=>{
            response.statusCode=400
            response.end();
        })
    }

    request.on('end',()=>{
        if(bufferList.length>0){
            ctx.rawBody=Buffer.concat(bufferList,totalLength);
            bufferList=[];
        }

        let ctype=ctx.headers['content-type'] || ''
        if(ctx.rawBody && ctype.indexOf('multipart/form-data')==0){ //不用==的原因是content-type的属性值后面还可能带上';charset=utf-8'或其他
            ctx.files=parseBody(ctx.rawBody,ctx.headers);
        }

        rm[pathname](ctx);
    })

})

serv.listen(3456);